// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>
#include "protoserv/compiler/java/java_service_skeleton.h"
#include "protoserv/compiler/java/java_helpers.h"

namespace protoserv {
namespace compiler {
namespace java {

ServiceSkeletonGenerator::ServiceSkeletonGenerator(
      const ServiceDescriptor* descriptor)
  : descriptor_(descriptor) {}

ServiceSkeletonGenerator::~ServiceSkeletonGenerator() {}

void ServiceSkeletonGenerator::Generate(io::Printer* printer) {
  bool is_own_file = descriptor_->file()->options().java_multiple_files();
  map<string, string> vars;
  vars["classname"] = descriptor_->name();
  vars["CLASSNAME"] = ToUpperCase(descriptor_->name());
  vars["fileclass"] = ClassName(descriptor_->file()) + "Rpc";
  vars["static"] = is_own_file ? "" : "static";
  printer->Print(vars,
    "public $static$ final class $classname$Skeleton extends"
    " com.googlecode.protoserv.ServiceSkel {\n"
    "  private $classname$ implementor;\n"
    "\n"
    "  public $classname$Skeleton($classname$ implementor) {\n"
    "    this.implementor = implementor;\n"
    "  }\n"
    "\n"
    "  @Override\n"
    "  public void registerExtensions(\n"
    "      com.google.protobuf.ExtensionRegistry registry) {\n"
    "    $fileclass$.registerAllExtensions(registry);\n"
    "  }\n"
    "\n");
  printer->Indent();

  // getMethodIds().
  printer->Print(vars,
    "@Override\n"
    "public int[] getMethodIds() {\n"
    "  return new int[] {\n");
  printer->Indent();
  printer->Indent();
  for (int i = 0; i < descriptor_->method_count(); i++) {
    const MethodDescriptor* method = descriptor_->method(i);
    vars["METHOD"] = ToUpperCase(method->name());
    printer->Print(vars,
        "$fileclass$.$CLASSNAME$_$METHOD$_INPUT_FIELD_NUMBER,\n");
  }
  printer->Outdent();
  printer->Outdent();
  printer->Print(
      "  };\n"
      "}\n"
      "\n");

  // run().
  printer->Print(vars,
    "@Override\n"
    "public boolean run(\n"
    "    int methodId,\n"
    "    com.googlecode.protoserv.RpcHandler rpc,\n"
    "    com.googlecode.protoserv.Protocol.RequestProto request,\n"
    "    com.googlecode.protoserv.Protocol.ResponseProto.Builder response) {\n"
    "  switch (methodId) {\n");
  printer->Indent();
  printer->Indent();
  for (int i = 0; i < descriptor_->method_count(); i++) {
    const MethodDescriptor* method = descriptor_->method(i);
    vars["METHOD"] = ToUpperCase(method->name());
    vars["method"] = UnderscoresToCamelCase(method);
    printer->Print(vars,
        "case $fileclass$.$CLASSNAME$_$METHOD$_INPUT_FIELD_NUMBER:\n"
        "  $method$(rpc, request, response);\n"
        "  return true;\n");
  }
  printer->Outdent();
  printer->Outdent();
  printer->Print(
      "  }\n"
      "  return false;\n"
      "}\n"
      "\n");

  // methods.
  vars["extension_prefix"] = ClassName(descriptor_->file()) + "Rpc." +
      UnderscoresToCamelCase(descriptor_);
  for (int i = 0; i < descriptor_->method_count(); i++) {
    const MethodDescriptor* method = descriptor_->method(i);
    vars["method"] = UnderscoresToCamelCase(method);
    vars["Method"] = method->name();
    vars["inputclass"] = ClassName(method->input_type());
    vars["outputclass"] = ClassName(method->output_type());
    printer->Print(vars,
      "private void $method$(\n"
    "    com.googlecode.protoserv.RpcHandler rpc,\n"
      "    com.googlecode.protoserv.Protocol.RequestProto request,\n"
      "    com.googlecode.protoserv.Protocol.ResponseProto.Builder response) {\n"
      "  $inputclass$ input = request.getExtension(\n"
      "      $extension_prefix$$Method$Input);\n"
      "  $outputclass$.Builder output =\n"
      "      $outputclass$.newBuilder();\n"
      "  this.implementor.$method$(rpc, input, output);\n"
      "  if (rpc.getStatus() ==\n"
      "      com.googlecode.protoserv.Protocol.RpcStatus.SUCCESS) {\n"
      "    response.setExtension(\n"
      "        $extension_prefix$$Method$Output,\n"
      "        output.build());\n"
      "  }\n"
      "}\n");
  }

  printer->Outdent();
  printer->Print("}\n\n");
}

}  // namespace java
}  // namespace compiler
}  // namespace protoserv
